//
// action.cpp for  in /home/jousse_f//workspace/C++/abstractVM
// 
// Made by florian jousseau
// Login   <jousse_f@epitech.net>
// 
// Started on  Mon Feb 13 11:54:58 2012 florian jousseau
// Last update Fri Feb 17 10:15:04 2012 florian jousseau
//

#include "action.hh"
#include "Parser.hh"
#include "Exception.hh"
#include "checkStack.hh"
#include "convert.hpp"

const t_type tabPtr[] =
  {
    {"pop", doPop},
    {"dump", doDump},
    {"add", doAdd},
    {"sub", doSub},
    {"mul", doMul},
    {"div", doDiv},
    {"mod", doMod},
    {"print", doPrint},
    {"exit", doExit},
    {"push", doPush},
    {"assert", doAssert},
    {"", NULL}
  };

void		action(Parser *p, VM &vm)
{
  int		i = 0;

  vm.setParser(p);
  if (vm.getParser()->getCatch() == 1)
    return;
  while ((tabPtr[i].str != vm.getParser()->getInstr()) && (tabPtr[i].str != ""))
    i++;
  if (tabPtr[i].str != "")
    tabPtr[i].ptr(vm);
}

void		doPush(VM &vm)
{
  eOperandType	type;
  IOperand	*newOp;

  type = selectType(vm.getParser()->getValue());
  if (type == FLOAT || type == DOUBLE)
    newOp = vm.createOperand(type, vm.getParser()->getZ());
  else
    newOp = vm.createOperand(type, vm.getParser()->getN());
  vm.stack.push_front(newOp);
}

void		doPop(VM &vm)
{
  if (checkStack(1, vm.stack))
    vm.stack.pop_front();
  else
    throw AbstractEx("[Error] Stack is empty");
}

void		doAssert(VM &vm)
{
  std::list<IOperand *>::const_iterator i =  vm.stack.begin();
  eOperandType	type;

  type = selectType(vm.getParser()->getValue());
  if (type == FLOAT || type == DOUBLE)
    {
      std::string str = vm.getParser()->getZ();
      if (type != (*i)->getType() || str != (*i)->toString())
	throw AbstractEx("[Error] Bad Assert");
    }
  else
    {
      std::string str = vm.getParser()->getN();
      if (type != (*i)->getType() || str != (*i)->toString())
	throw AbstractEx("[Error] Bad Assert");
    }
}

void		doDump(VM &vm)
{
  std::list<IOperand *>::const_iterator i =  vm.stack.begin();

  while (i != vm.stack.end())
    {
      std::string tmp = (*i)->toString();
      size_t it = tmp.size() - 2;
      int y = tmp.find(".0", it);
      if (y != -1)
	tmp.erase(it);
      std::cout << tmp << std::endl;
      //std::cout << (*i)->toString() << std::endl;
      i++;
    }
}

void		doAdd(VM &vm)
{
  if (!checkStack(2, vm.stack))
    throw AbstractEx("[Error][add] Not enough number in the stack");

  IOperand	*nb1 = vm.stack.front();
  vm.stack.pop_front();
  IOperand	*nb2 = vm.stack.front();
  vm.stack.pop_front();
  IOperand	*tmp;

  tmp = (*nb2) + (*nb1);

  IOperand *newOp = vm.createOperand(findType(nb1, nb2), tmp->toString());
  vm.stack.push_front(newOp);
  delete tmp;
}

void		doSub(VM &vm)
{
  if (!checkStack(2, vm.stack))
    throw AbstractEx("[Error][sub] Not enough number in the stack");

  IOperand	*nb1 = vm.stack.front();
  vm.stack.pop_front();
  IOperand	*nb2 = vm.stack.front();
  vm.stack.pop_front();
  IOperand	*tmp;

  tmp = (*nb2) - (*nb1);

  IOperand *newOp = vm.createOperand(findType(nb1, nb2), tmp->toString());
  vm.stack.push_front(newOp);
  delete tmp;
}

void		doMul(VM &vm)
{
  if (!checkStack(2, vm.stack))
    throw AbstractEx("[Error][mul] Not enough number in the stack");

  IOperand	*nb1 = vm.stack.front();
  vm.stack.pop_front();
  IOperand	*nb2 = vm.stack.front();
  vm.stack.pop_front();
  IOperand	*tmp;
  tmp = (*nb2) * (*nb1);

  IOperand *newOp = vm.createOperand(findType(nb1, nb2), tmp->toString());
  vm.stack.push_front(newOp);
  delete tmp;
}

void		doDiv(VM &vm)
{
  if (!checkStack(2, vm.stack))
    throw AbstractEx("[Error][div] Not enough number in the stack");

  IOperand	*nb1 = vm.stack.front();
  vm.stack.pop_front();
  IOperand	*nb2 = vm.stack.front();
  vm.stack.pop_front();

  if (!checkDivZero(toNumber<double>(nb1->toString())))
    throw AbstractEx("[Error][div] Try to divide by zero.");

  IOperand	*tmp;

  tmp = (*nb2) / (*nb1);

  IOperand *newOp = vm.createOperand(findType(nb1, nb2), tmp->toString());
  vm.stack.push_front(newOp);
  delete tmp;
}

void		doMod(VM &vm)
{
  if (!checkStack(2, vm.stack))
    throw AbstractEx("[Error][mod] Not enough number in the stack");

  IOperand	*nb1 = vm.stack.front();
  vm.stack.pop_front();
  IOperand	*nb2 = vm.stack.front();
  vm.stack.pop_front();
  IOperand	*tmp;

  if (!checkDivZero(toNumber<double>(nb1->toString())))
    throw AbstractEx("[Error][div] Try to do modulo by zero.");

  tmp = (*nb2) % (*nb1);

  IOperand *newOp = vm.createOperand(findType(nb1, nb2), tmp->toString());
  vm.stack.push_front(newOp);
  delete tmp;
}

void		doPrint(VM &vm)
{
  std::list<IOperand *>::const_iterator i =  vm.stack.begin();

  if ((*i)->getType() != INT8)
    throw AbstractEx("[Error] Bad type Print");
  char c = toNumber<int>((*i)->toString());
  std::cout << c << std::endl;
}

void		doExit(VM &vm)
{
  (void)vm;
  throw AbstractEx("");
}
